package com.hundsun.epay.autosimu.api;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.hundsun.epay.autosimu.config.Channel;
import com.hundsun.epay.autosimu.config.ControllerField;
import com.hundsun.epay.autosimu.config.PayCorporation;
import com.hundsun.epay.autosimu.config.Trade;
import com.hundsun.epay.autosimu.enums.ControllerFieldType;
import com.hundsun.epay.autosimu.internal.ObligateInnerVariableName;
import com.hundsun.epay.autosimu.internal.rules.FieldControlRefRules;
import com.hundsun.epay.autosimu.internal.rules.FieldControlRules;
import com.hundsun.epay.autosimu.internal.rules.FieldLocationRules;
import com.hundsun.epay.autosimu.internal.rules.Rules;
import com.hundsun.epay.autosimu.utils.StringUtility;

/**
 * 字段控制器内省
 * @author Clown
 */
public abstract class IntrospectedFieldControl {
	
	protected enum InternalAttributes{
		ATTR_CONTROL_ID,
		ATTR_CONTROL_TYPE,
		ATTR_CONTROL_SEPARATOR,
		ATTR_CONTROL_KEY_RULE,
		ATTR_CONTROL_KEY_NAME,
		ATTR_CONTROL_RULES,
		ATTR_CONTROL_IS_REF,
		ATTR_CONTROL_REF,
		ATTR_INNER_VARIABLE_NAME,
		ATTR_OVERTIME_RULE,
		ATTR_EXCEPTION_RULE,
		;
	}
	protected Map<IntrospectedFieldControl.InternalAttributes, Object> internalAttributes;
	protected PayCorporation payCorpConfig;
	protected Channel channelConfig;
	protected Trade tradeConfig;
	protected ControllerField fieldConfig;
	
	public IntrospectedFieldControl() {
		super();
		internalAttributes = new HashMap<IntrospectedFieldControl.InternalAttributes, Object>();
	}

	/**
	 * @return the payCorpConfig
	 */
	public PayCorporation getPayCorpConfig() {
		return payCorpConfig;
	}

	/**
	 * @param payCorpConfig the payCorpConfig to get
	 */
	public void setPayCorpConfig(PayCorporation payCorpConfig) {
		this.payCorpConfig = payCorpConfig;
	}

	/**
	 * @return the channelConfig
	 */
	public Channel getChannelConfig() {
		return channelConfig;
	}

	/**
	 * @param channelConfig the channelConfig to set
	 */
	public void setChannelConfig(Channel channelConfig) {
		this.channelConfig = channelConfig;
	}

	/**
	 * @return the tradeConfig
	 */
	public Trade getTradeConfig() {
		return tradeConfig;
	}

	/**
	 * @param tradeConfig the tradeConfig to set
	 */
	public void setTradeConfig(Trade tradeConfig) {
		this.tradeConfig = tradeConfig;
	}

	/**
	 * @return the fieldConfig
	 */
	public ControllerField getFieldConfig() {
		return fieldConfig;
	}

	/**
	 * @param fieldConfig the fieldConfig to set
	 */
	public void setFieldConfig(ControllerField fieldConfig) {
		this.fieldConfig = fieldConfig;
	}
	
	public void initialize(){
		calculateControlId();
		calculateControlType();
		calculateRefControl();
		calculateControlKey();
		calculateControlRules();
		calculateControlRuleSeparator();
		calculateInnerVariableName();
	}
	
	/**计算控制id*/
	protected void calculateControlId(){
		setFieldControlId(this.tradeConfig.getId() + "." + this.fieldConfig.getId());
	}
	
	/**处理控制类型*/
	protected void calculateControlType(){
		setFieldControlType(this.fieldConfig.getType());
	}
	
	/**处理引用控制*/
	protected void calculateRefControl(){
		if(StringUtility.stringHasValue(this.fieldConfig.getRefField())){
			setFieldControlIsRef(true);
			String [] refStrs = this.fieldConfig.getRefField().split(",");
			String currentTradeId = null;
			for (String refStr : refStrs) {
				FieldControlRefRules rule = new FieldControlRefRules(refStr, currentTradeId);
				currentTradeId = rule.getTradeId();
				addFieldControlRef(rule);
			}
		}
	}
	
	/**处理控制字段定位*/
	protected void calculateControlKey(){
		if(StringUtility.stringHasValue(this.fieldConfig.getKey())){
			FieldLocationRules rule = new FieldLocationRules(this.fieldConfig.getKey());
			setFieldControlKeyRule(rule);
			setFieldControlKeyName(rule.getName());
		}
	}
	
	/**处理控制规则*/
	protected void calculateControlRules(){
		String sourceRulesStr = this.fieldConfig.getRule();
		if(StringUtility.stringHasValue(sourceRulesStr)) {
			String[] ruleStrs = sourceRulesStr.split(";");
			for (String ruleStr : ruleStrs) {
				FieldControlRules rule = new FieldControlRules(ruleStr);
				if(rule.isDefineOvertime()) {
					setOvertimeRule(rule);
				}else if(rule.isDefineException()) {
					setExceptionRule(rule);
				}
				addFieldControlRule(rule);
			}
		}
	}
	
	/**处理请求控制分隔符*/
	protected void calculateControlRuleSeparator(){
		setFieldControlSeparator(this.fieldConfig.getSeparator());
	}
	
	public void setFieldControlId(String id){
		this.internalAttributes.put(InternalAttributes.ATTR_CONTROL_ID, id);
	}
	
	public void setFieldControlType(ControllerFieldType type){
		this.internalAttributes.put(InternalAttributes.ATTR_CONTROL_TYPE, type);
	}
	
	public void setFieldControlSeparator(String separator){
		this.internalAttributes.put(InternalAttributes.ATTR_CONTROL_SEPARATOR, separator);
	}
	
	public void setFieldControlKeyRule(Rules rule){
		this.internalAttributes.put(InternalAttributes.ATTR_CONTROL_KEY_RULE, rule);
	}
	
	public void setFieldControlKeyName(String name){
		this.internalAttributes.put(InternalAttributes.ATTR_CONTROL_KEY_NAME, name);
	}
	
	public void addFieldControlRule(Rules rule){
		List<Rules> rules = (List<Rules>) internalAttributes.get(InternalAttributes.ATTR_CONTROL_RULES);
		if(rules == null){
			rules = new ArrayList<Rules>();
			internalAttributes.put(InternalAttributes.ATTR_CONTROL_RULES, rules);
		}
		rules.add(rule);
	}
	
	public void setFieldControlIsRef(boolean isRef){
		this.internalAttributes.put(InternalAttributes.ATTR_CONTROL_IS_REF, isRef);
	}
	
	public void addFieldControlRef(FieldControlRefRules rule){
		List<Rules> rules = (List<Rules>) internalAttributes.get(InternalAttributes.ATTR_CONTROL_REF);
		if(rules == null){
			rules = new ArrayList<Rules>();
			internalAttributes.put(InternalAttributes.ATTR_CONTROL_REF, rules);
		}
		rules.add(rule);
	}
	
	public String getFieldControlId(){
		return (String) this.internalAttributes.get(InternalAttributes.ATTR_CONTROL_ID);
	}
	
	public ControllerFieldType getFieldControlType(){
		return (ControllerFieldType) this.internalAttributes.get(InternalAttributes.ATTR_CONTROL_TYPE);
	}
	
	public String getFieldControlSeparator(){
		return (String) this.internalAttributes.get(InternalAttributes.ATTR_CONTROL_SEPARATOR);
	}
	
	public Rules getFieldControlKeyRule(){
		return (Rules) this.internalAttributes.get(InternalAttributes.ATTR_CONTROL_KEY_RULE);
	}
	
	public String getFieldControlKeyName(){
		return (String) this.internalAttributes.get(InternalAttributes.ATTR_CONTROL_KEY_NAME);
	}
	
	public List<Rules> getFieldControlRule(){
		return (List<Rules>) internalAttributes.get(InternalAttributes.ATTR_CONTROL_RULES);
	}
	
	public boolean getFieldControlIsRef(){
		Boolean isRef = (Boolean) this.internalAttributes.get(InternalAttributes.ATTR_CONTROL_IS_REF);
		return isRef == null ? false : isRef;
	}
	
	public List<Rules> getFieldControlRef(){
		return (List<Rules>) this.internalAttributes.get(InternalAttributes.ATTR_CONTROL_REF);
	}
	
	public void setInnerVariableName(String name){
		this.internalAttributes.put(InternalAttributes.ATTR_INNER_VARIABLE_NAME, name);
	}
	
	public String getInnerVaribaleName(){
		return (String) this.internalAttributes.get(InternalAttributes.ATTR_INNER_VARIABLE_NAME);
	}
	
	public void setOvertimeRule(FieldControlRules rule) {
		if(!internalAttributes.containsKey(InternalAttributes.ATTR_OVERTIME_RULE)) {
			this.internalAttributes.put(InternalAttributes.ATTR_OVERTIME_RULE, rule);
		}
	}
	
	public FieldControlRules getOvertimeRule() {
		return (FieldControlRules) this.internalAttributes.get(InternalAttributes.ATTR_OVERTIME_RULE);
	}
	
	public boolean fieldControlOvertime() {
		return getOvertimeRule() == null;
	}
	
	public void setExceptionRule(FieldControlRules rule) {
		if(!internalAttributes.containsKey(InternalAttributes.ATTR_EXCEPTION_RULE)) {
			this.internalAttributes.put(InternalAttributes.ATTR_EXCEPTION_RULE, rule);
		}
	}
	
	public FieldControlRules getExceptionRule() {
		return (FieldControlRules) this.internalAttributes.get(InternalAttributes.ATTR_EXCEPTION_RULE);
	}
	
	public boolean fieldControlException() {
		return getExceptionRule() == null;
	}
	
	/**
	 * 获取请求参数提取到内部变量的变量名
	 * @param index
	 * @return
	 */
	protected void calculateInnerVariableName(){
//		return "_innerVarForRequest" + index;
		ControllerFieldType type = this.getFieldControlType();
		if(type == ControllerFieldType.REF){
			return;
		}
		//内部字段名称均以_开头
		if(type == ControllerFieldType.ORDER_NO) {
			setInnerVariableName(ObligateInnerVariableName.OBLIGATE_ORDER_NO);
		}else if(type == ControllerFieldType.DETAIL_NO) {
			setInnerVariableName(ObligateInnerVariableName.OBLIGATE_DETAIL_NO);
		}else if(type == ControllerFieldType.BACKURL){
			setInnerVariableName(ObligateInnerVariableName.OBLIGATE_BACK_URL);
		}else if(type == ControllerFieldType.FRONTURL){
			setInnerVariableName(ObligateInnerVariableName.OBLIGATE_FRONT_URL);
		}else{
			StringBuilder sb = new StringBuilder();
			appendParentName(this.getFieldControlKeyRule(), sb);
			//使用驼峰命名
			setInnerVariableName("_" + StringUtility.lineToCamel(sb.toString()));
		}
	}
	
	private void appendParentName(Rules rule, StringBuilder sb) {
		if(rule.getParent() != null) {
			appendParentName(rule.getParent(), sb);
			sb.append("_");
		}
		sb.append(rule.getName());
		//指定下标的数组内控制字段，名称命名为name+index
		if(rule.isInArray() && !rule.isRepeat()){
			sb.append(rule.getIndex());
		}
	}
}
